Skip to content

feat: unified Tools upscale for images and videos - #129

Merged
IAnMove merged 4 commits into
mainfrom
feat/tools-image-upscale
Sep 4, 2026
Merged

feat: unified Tools upscale for images and videos#129
IAnMove merged 4 commits into
mainfrom
feat/tools-image-upscale

Conversation

@IAnMove

@IAnMove IAnMove commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Resumen ejecutivo

  • Herramientas → Reescalar mantiene una única acción y selector para imágenes y vídeos.
  • Las imágenes se enrutan al adaptador spatial upsampler en modo still y producen un PNG derivado; los vídeos conservan FlashVSR/Lanczos, audio y salida de vídeo.
  • El picker de imágenes para Reescalar y Quitar fondo usa tarjetas con miniaturas, selección resaltada, tipo/dimensiones, preview grande, upload y usar elemento actual. Revoice sigue limitado a vídeo.

Detalle técnico

  • Validación compartida de asset IDs, kind, extensiones, workspaces, URLs virtuales y confinamiento de rutas; las fuentes nunca se sobrescriben.
  • Activity, tareas canónicas, polling, cancelación, simulación y errores reutilizan el contrato existente.
  • Sidecars/manifest incluyen capability, provenance, inputs, parents, transformations, source kind/id y detalles técnicos del PNG.
  • Tests UI/E2E cubren selección de tarjetas, ausencia de <select> de catálogo, preview/dimensiones, endpoint/payload correcto, regresión vídeo, cancelación, fallo y asset derivado simulado.

Validación

  • npm run i18n:check
  • npm run lint -- --max-warnings=0
  • npm run build
  • npm run test — 605/605 ✅
  • E2E Tools/Background/Upscale — 4/4 ✅ (sin medios reales)
  • python -m py_compile ...
  • python scripts/verify_clean_repo.py
  • git diff --check
  • python scripts/code_health.py --check --markdown ⚠️ ratchet rojo por drift del baseline compartido (LOC +7,591 vs presupuesto +7,010; funciones complejas +10 vs +5); no se relajó ni editó baseline.

Informe de coste

  • Tests simulados: 0 tokens externos.
  • Generaciones de medios reales: 0.
  • Proveedores/remotos de pago: 0.
  • Modelos nuevos descargados: 0.

Please request Cursor review for the unified picker and image/video routing.


Note

Medium Risk
Large changes to GPU tool workers and filesystem source resolution in _launch_runtime.py; mistakes could affect path safety or job outputs, though the pattern mirrors the existing remove-background boundary.

Overview
Tools Upscale is now one post-processing action for stills and clips: images go through a new still path (perform_spatial_upsampling with still_image=True) and write a derived PNG; videos keep the existing FlashVSR/Lanczos pipeline with audio remux.

The backend adds shared _resolve_tool_source (same confinement rules as remove-background), richer tool sidecars/manifests (lineage, capability, provenance), and stricter upscale/revoice API validation. Revoice adopts the same source resolver and sidecar fields without changing its video-only behavior.

The Studio Tools UI replaces the image <select> with a thumbnail asset grid for Upscale and Remove background, accepts image or video uploads for Upscale, and submits source + source_kind instead of only video_path. Docs, contract tests, and E2E mocks cover the new endpoint shape.

Reviewed by Cursor Bugbot for commit 089bcd2. Configure here.

@IAnMove

IAnMove commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

@cursor Please review this PR, with focus on the unified visual image picker, source kind/workspace validation, and the guarantee that image Upscale never enters the video pipeline. The PR includes provider-free simulated E2E coverage.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

PR Review — Loreframe Studio

Risk: medium
Scope: 19 file(s); +1350/-187; GitHub workflows, React UI, backend services, docs, generation pipeline, repo scripts / CI

Automated review from scripts/analyze_pr.py. This is a heuristic pass (no LLM) so humans still own the merge decision.

Findings

  • medium — Large pull request
    1350 additions / 187 deletions. Reviewers will have an easier time with smaller, focused PRs.
  • medium — Very large file change (app/_launch_runtime.py)
    app/_launch_runtime.py adds 628 lines. Consider splitting the PR.
  • low — UI changed — rebuild before merge
    Run cd ui && npm run build (CI already does this). Pinokio Update rebuilds for end users; keep ui/dist untracked.

Changed files

  • added: tests/test_tools_upscale_contract.py
  • modified: .github/workflows/ci.yml, app/_launch_runtime.py, app/docs/API.md, app/services/generation_provenance.py, app/shared/tools/background_removal_request.py, app/wgp.py, docs/development/CODE_HEALTH.md, scripts/code_health.py, tests/fixtures/architecture_wire_inventory.json, ui/e2e/helpers/apiRoutes.ts, ui/e2e/specs/tools-background-removal.spec.ts, ui/src/api/generation.ts, ui/src/components/Sidebar/ToolsPanel.tsx, ui/src/components/Sidebar/ToolsSourcePanel.tsx, ui/src/i18n/locales/en/studio.json, ui/src/i18n/locales/es/studio.json, ui/src/stores/useStore.ts, ui/tests/toolsPanel.test.tsx

CONTRIBUTING checklist

  • python scripts/verify_clean_repo.py
  • python -m compileall -q app/services app/launch.py scripts
  • cd ui && npm run build if the UI changed
  • No weights, CivitAI sidecars, or generated guides
  • Stays local-first (no required accounts / telemetry)

Posted by the repo PR review workflow. Re-runs on each push to the PR.

@cursor

cursor Bot commented Sep 4, 2026

Copy link
Copy Markdown

Revisé el PR #129 (feat/tools-image-upscale) centrado en el picker, la validación de source_kind/workspace y que un Upscale de imagen no entre al pipeline de vídeo. El comentario completo está en el PR.

El aislamiento HTTP+UI se sostiene. La UI no envía video_path si source_kind === 'image'; el resolver rechaza kind/extensión/asset cruzados; el job guarda source_path o video_path; el worker de imagen usa _upscale_tool_image(..., still_image=True) y escribe PNG. Los tests AST confirman que esa rama no llama al decoder/remux de vídeo.

No lo daría por cerrado. El worker hace source_kind or "video" y vuelve a resolver con _resolve_tool_clip_path (más laxo). “Usar imagen/clip actual” no manda workspace ni assetId. Tras un upscale, el picker no refresca (toolsAssetsRevision solo sube en Quitar fondo). El resumen del PR afirma E2E de regresión vídeo, cancelación y fallo; solo existe el happy path de imagen (los modos cancel/fail del stub no se usan).

El catálogo visual es solo imágenes: un vídeo de librería no se elige por tarjeta. El ratchet de code health sigue rojo por drift compartido; no es un fallo de este cambio.

Open in Web Open in Cursor 

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Code health

Quality score: 49.0/100

Higher is better. The score is a trend dashboard; the independent ratchet below remains the CI gate.

Component Weight Current Change
Cyclomatic health 45% 52.2 -0.3
File concentration 25% 53.6 -0.7
Oversized-file debt 20% 29.3 -0.1
Modularity 10% 62.4 -0.2

Change vs PR base: -0.3 points.

Metric Value
Production LOC 241,256
Production files 531
Test LOC 69,994
Functions measured 15,077
Functions complexity ≥ 15 784
Maximum complexity 667

Markdown, JSON catalogs and tests are out of this table. Only app/ runtime + ui/src TS/JS count.

Most complex functions

Complexity Where
667 app/wgp.py:7164 generate_video
374 ui/src/stores/useStore.ts:4021 Async method 'startGeneration'
355 app/_launch_runtime.py:23773 _run_generation
308 app/wgp.py:12281 generate_video_tab
271 ui/src/components/Sidebar/SceneAnimatorPanel.tsx:474 Function 'SceneAnimatorPanel'
266 ui/src/stores/useStore.ts:8563 Async method 'loadSettingsFromOutput'
258 app/services/director/planners/short_film.py:3433 ShortFilmPlanner._plan_story_driven
248 app/services/director_pipeline.py:13735 _run_video_generation
245 app/services/director_pipeline.py:7860 _run_pipeline
243 ui/src/features/agent/agentActions.ts:1109 Function 'parseAction'
226 app/services/director_pipeline.py:6689 update_comic_preview
225 ui/src/features/agent/agentActions.ts:2766 Async function 'executeAgentActions'

Trend vs baseline

Metric Δ
Production LOC +661
Test LOC +416
Functions ≥ 15 +5
Maximum complexity +0

Warnings

  • production LOC increased by +661
  • functions at complexity >= 15 increased by +5
  • complexity hotspot ui/src/components/Sidebar/ToolsPanel.tsx rose 19 -> 21
  • hotspot app/_launch_runtime.py increased by +526 lines
  • hotspot app/wgp.py increased by +7 lines
  • hotspot ui/src/stores/useStore.ts increased by +9 lines

Ratchet passed.

Comment thread app/_launch_runtime.py
Comment thread ui/src/stores/useStore.ts
@IAnMove

IAnMove commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

cursor review

@cursor

cursor Bot commented Sep 4, 2026

Copy link
Copy Markdown

Cursor review — picker unificado, validación y aislamiento imagen/vídeo

Revisé el commit 089bcd2 con el foco pedido: picker visual, validación de source_kind/workspace, y la garantía de que un Upscale de imagen no entre en el pipeline de vídeo. Los E2E son stubs de API (sin proveedores ni medios reales).

Veredicto

El enrutado HTTP + UI sí aísla imagen y vídeo en el camino feliz. No apruebo el merge tal cual: hay un default peligroso en el worker, un hueco de identidad al usar el ítem actual, y la cobertura E2E no coincide con lo que afirma el PR.


1. Picker visual unificado

Bien

  • Reescalar y Quitar fondo comparten la rejilla de miniaturas; no hay <select> de catálogo.
  • Tarjetas con aria-pressed, tipo/dimensiones (Image · 1920×1080), preview grande, upload y “usar actual”.
  • Revoice sigue siendo solo vídeo (el botón queda disabled con una imagen).
  • submitToolUpscale no envía video_path cuando source_kind === 'image'.

Huecos

  • El catálogo visual es solo imágenes (fetchAssets({ kind: 'image' })). Un vídeo de librería no se puede elegir por tarjeta; solo upload o “usar clip actual”. El selector unificado no es simétrico.
  • useCurrentImage / useCurrentClip no mandan workspace ni assetId. El backend trata el fichero como unmanaged y resuelve por nombre en el workspace activo → colisión o 404 si el mismo nombre vive en otro workspace.
  • Tras un upscale, toolsAssetsRevision solo se incrementa en Quitar fondo. El PNG derivado no aparece en el picker hasta remount.
  • El stub E2E responde 404 a /api/v1/file/…, así que las miniaturas del test son <img> rotas; solo se afirma visibilidad.
  const useCurrentImage = () => {
    if (currentIsImage) setSource({ path: current.name, name: current.name, url: current.url, kind: 'image' })
  }
          if (status.status === 'completed') {
            clearInterval(pollInterval)
            if (removingBackground) set(st => ({ toolsAssetsRevision: st.toolsAssetsRevision + 1 }))

2. Validación de kind / workspace

Bien (capa HTTP)

  • _resolve_tool_source rechaza paths conflictivos (409), asset_id mal formado, kind ≠ asset, kind ≠ extensión, traversal y workspaces desconocidos.
  • Reutiliza resolve_source / destination_context (mismo confinamiento que Quitar fondo).
  • El job queda con source_path o video_path según kind, nunca ambos.
  • Tests de contrato cubren still vs vídeo y rechazo de un asset image con expected_kind="video" (400).

Riesgo en el worker

  • Tras resolver en HTTP, _run_tool_upscale vuelve a resolver con _resolve_tool_clip_path (cualquier absoluto existente, sin confinamiento). En jobs creados por HTTP el path ya es absoluto; en jobs recuperados/rehidratados la puerta es más débil.
  • Default source_kind or "video": si faltan params, un still entra en get_video_info / remux.
            source_kind = str(params.get("source_kind") or "video").casefold()
            source_value = (
                params.get("source_path")
                if source_kind == "image" else params.get("video_path")
            ) or params.get("_source_path") or params.get("source")
            source_path = _resolve_tool_clip_path(source_value, workspace)
            ...
            if source_extension not in _TOOL_SOURCE_EXTENSIONS[source_kind]:
                raise ValueError("Source kind does not match file format")

3. Garantía: imagen ↛ pipeline de vídeo

En el camino HTTP+UI la garantía se sostiene:

Capa Qué impide el cruce
UI source_kind: 'image' y sin video_path
HTTP kind + extensión + kind del asset; job source_path XOR video_path
Worker if source_kind == "image"_upscale_tool_image(..., still_image=True) (PNG)
Simulado generation_mode=source_kindcreate_artifact escribe .png vs .mp4
AST la rama image no llama get_video_info / extract_audio_tracks / get_resampled_video / save_video

Los tests AST son estructurales, no runtime. FlashVSR still usa el adaptador espacial con still_image=True y escribe PNG con PIL; no decodifica ni remuxea vídeo. Eso es el diseño, no el pipeline de clip.

El agujero real es el default "video" + re-resolve laxo del worker. Debería fallar cerrado (source_kind obligatorio) y reutilizar el path ya confinado.


4. E2E simulados (sin proveedor)

Cierto: el happy path de imagen está mockeado (sin tokens, sin GPU, sin medios reales). Afirma source + source_kind: image, ausencia de video_path, y el asset derivado hero_upscaled.png.

La descripción del PR sobreafirma. Dice que los E2E cubren “regresión vídeo, cancelación, fallo”. En el spec hay un test de upscale (complete). upscaleMode: 'cancel' | 'fail' existe en apiRoutes.ts y no se usa. No hay E2E que elija un clip y compruebe video_path + source_kind: video. Quitar fondo sí tiene cancel/fail; upscale no.

Antes de merge, o se añaden esos tres casos (el stub ya los soporta) o se corrige el resumen del PR.


Follow-ups recomendados (no bloquean el aislamiento HTTP)

  1. Default de worker: exigir source_kind y no caer a "video".
  2. Propagar workspace / assetId en “usar imagen/clip actual”.
  3. Incrementar toolsAssetsRevision también al completar un upscale de imagen.
  4. E2E: regresión vídeo + cancel + fail, o alinear el texto del PR.
  5. (Opcional) catálogo de vídeos en la rejilla si el selector unificado debe ser simétrico.

Ratchet de code health: rojo por drift compartido (LOC / funciones complejas). No es un fallo de este cambio; no relajar el baseline aquí.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 089bcd2. Configure here.

@IAnMove
IAnMove merged commit df146f2 into main Sep 4, 2026
4 checks passed
@IAnMove
IAnMove deleted the feat/tools-image-upscale branch September 5, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant